﻿using Abp.Application.Services.Dto;
using Abp.Domain.Repositories;
using Abp.Events.Bus;
using Abp.Linq.Extensions;
using Abp.Runtime.Caching;
using AutoMapper.QueryableExtensions;
using Microsoft.EntityFrameworkCore;
using NetCore.Authorization.Users;
using NetCore.Entities.System;
using NetCore.Enums;
using NetCore.Events;
using NetCore.Extensions;
using NetCore.Interactive.Dtos;
using NetCore.LinqExtensions;
using NetCore.Redis;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.AutoMapper;

namespace NetCore.Interactive
{
    /// <summary>
    /// 交互中心/消息通知
    /// </summary>
    //[AbpAuthorize]
    public class InteractiveAppService : NetCoreAppServiceBase, IInteractiveAppService
    {
        public IEventBus EventBus { get; set; }
        private readonly IRepository<User, long> _userRepository;
        private readonly ICacheManager _cacheManager;
        private IRepository<Notify> _notifyRepository;
        public InteractiveAppService(
            ICacheManager cacheManager,
            IRepository<User, long> userRepository,
            IRepository<Notify> notifyRepository)
        {
            _cacheManager = cacheManager;
            _userRepository = userRepository;
            _notifyRepository = notifyRepository;
            EventBus = NullEventBus.Instance;
        }

        ///// <summary>
        ///// 依赖注入测试 可以实现
        ///// </summary>
        ///// <returns></returns>
        //public async Task<IList<NotifyDto>> GetAllNotifies()
        //{
        //    var result = await _notifyRepository.GetAllListAsync();
        //    return result.MapTo<List<NotifyDto>>();
        //}

        /// <summary>
        /// 获取消息列表
        /// 1、默认获取指向当前登录用户的所有消息
        /// </summary>
        /// <param name="input">含ProjectId的过滤项</param>
        /// <returns>消息列表</returns>
        public async Task<IPagedResult<NotifyDto>> GetNotifies(NotifyInput input)
        {
            var loginUserId = AbpSession.UserId;
            var pagedResult = await _notifyRepository.GetAll()
                    .AsNoTracking()
                    .WhereIf(input.ProjectId.HasValue, x => x.ProjectId == input.ProjectId)
                    .WhereIf(input.NotifyType.HasValue, x => x.NotifyType == input.NotifyType)
                    .WhereIf(input.Name.HasValue(), x => x.Title.Contains(input.Name))
                    .Where(x => x.ToUserId == loginUserId)
                    .OrderByDescending(m => m.ReceiveTime)
                    .ThenBy(x => x.IsRead)
                    .ProjectTo<NotifyDto>()
                    .ToPageResultAsync(input);
            return pagedResult;
        }

        /// <summary>
        /// 选定项全部标记为已读/未读
        /// </summary>
        /// <param name="input">含选中消息Id集合的过滤项</param>
        /// <returns></returns>
        public async Task<bool> ReadOrUnReadAll(NotifyReadOrUnReadAllInput input)
        {
            var loginUserId = AbpSession.UserId;
            var sysCount = 0;
            var appCount = 0;
            await _notifyRepository.GetAll()
                .WhereIf(input.NotifyType.HasValue, x => x.NotifyType == input.NotifyType)
                .WhereIf(input.SelectedIds.Any(), x => input.SelectedIds.Contains(x.Id))
                .Where(x => x.ToUserId == loginUserId && x.IsRead != input.ReadAll)
                .ForEachAsync(n =>
                {
                    n.IsRead = input.ReadAll;
                    if (n.NotifyType == NotifyType.系统消息)
                    {
                        sysCount++;
                    }
                    else
                    {
                        appCount++;
                    }
                });
            //这里需要更新用户未读消息数 这里触发事件更新
            if (loginUserId.HasValue)
            {
                EventBus.Trigger(new ReadNotifyEvent(loginUserId.Value, input.ReadAll ? -sysCount : sysCount));
                EventBus.Trigger(new ReadNotifyEvent(loginUserId.Value, input.ReadAll ? -appCount : appCount, NotifyType.应用消息));
            }
            return true;
        }

        /// <summary>
        /// 全部删除选中项
        /// </summary>
        /// <param name="input">含选中消息Id集合的过滤项</param>
        /// <returns></returns>
        public async Task<bool> DeleteAll(NotifyDeleteAllInput input)
        {
            var unReadNotifyList = await _notifyRepository.GetAll().Where(m => !m.IsRead && input.SelectedIds.Contains(m.Id))
                .GroupBy(n => n.NotifyType)
                .Select(o => new
                {
                    NotifyType = o.Key,
                    Count = o.Count()
                }).ToListAsync();
            await _notifyRepository.DeleteAsync(x => input.SelectedIds.Contains(x.Id));

            //删除之后，用户未读信息数也需要触发事件来更新
            if (AbpSession.UserId.HasValue && unReadNotifyList.Any())
            {
                unReadNotifyList.ForEach(m =>
                {
                    EventBus.Trigger(new ReadNotifyEvent(AbpSession.UserId.Value, -m.Count, m.NotifyType));
                });
            }
            return true;
        }

        /// <summary>
        /// 获取登录用户未读消息数 必须登录 直接使用用户的Dto字段
        /// </summary>
        /// <returns>登录用户未读消息数</returns>
        public async Task<NotifyCountOut> GetLoginUserNotifyCount()
        {
            var loginUserId = AbpSession.UserId;
            var key = $"{loginUserId}_Notify";

            #region 直接使用缓存

            var notifyCount = await _cacheManager.GetCache(NetCoreConsts.UserUnReadNotify).GetAsync($"{key}",
                async () =>
                {
                    var notifies = await _notifyRepository.GetAll().Where(m => m.ToUserId == loginUserId && !m.IsRead).ToListAsync();
                    return new NotifyCountOut
                    {
                        SystemNotifyCount = notifies.Count(m => m.NotifyType == NotifyType.系统消息),
                        ApplicationNotifyCount = notifies.Count(m => m.NotifyType == NotifyType.应用消息)
                    };
                });
            return notifyCount;

            #endregion

            #region 使用radis Api no pass

            //using (RedisHashService service = new RedisHashService())
            //{

            //    var systemNotifyCount = await GetOrSetNotifyCount(service, $"{NetCoreConsts.UserUnReadNotify}_{key}", "SystemNotifyCount");
            //    var applicationNotifyCount = await GetOrSetNotifyCount(service, $"{NetCoreConsts.UserUnReadNotify}_{key}", "ApplicationNotifyCount", NotifyType.应用消息);

            //    return new NotifyCountOut
            //    {
            //        SystemNotifyCount = systemNotifyCount >= 0 ? systemNotifyCount : 0,
            //        ApplicationNotifyCount = applicationNotifyCount >= 0 ? applicationNotifyCount : 0
            //    };
            //}

            #endregion
        }


        /// <summary>
        /// 消息只保留两个月 软删除
        /// </summary>
        /// <returns></returns>
        public async Task ClearDemoData()
        {
            var lastDays = DateTime.Now.AddDays(-60);
            var notifies = await _notifyRepository.GetAllListAsync(m => m.CreationTime < lastDays);
            await _notifyRepository.DeleteAsync(m => notifies.Contains(m));
            //await _notifyRepository.BulkDeleteAsync(notifies);
        }


        /// <summary>
        /// 获取设置缓存消息数
        /// </summary>
        /// <param name="service"></param>
        /// <param name="key"></param>
        /// <param name="dataKey"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        private async Task<int> GetOrSetNotifyCount(RedisHashService service, string key, string dataKey, NotifyType type = NotifyType.系统消息)
        {
            var count = 0;
            var isExist = await service.HashExistsAsync(key, dataKey);
            if (isExist)
            {
                count = await service.HashGetAsync<int>($"{NetCoreConsts.UserUnReadNotify}_{key}", dataKey);
            }
            else
            {
                var notifies = await _notifyRepository.GetAll().Where(m => m.ToUserId == AbpSession.UserId && !m.IsRead).ToListAsync();
                count = notifies.Count(m => m.NotifyType == type);
                await service.HashSetAsync($"{NetCoreConsts.UserUnReadNotify}_{key}", dataKey, count);
            }
            return count;
        }
    }
}
